package org.diy.traffic.daemon;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;

import org.diy.traffic.sinks.TrafficSink;
import org.diy.traffic.sinks.TrafficSink.TrafficSinkException;
import org.diy.traffic.updates.TrafficUpdate;
import org.diy.traffic.util.TLog;


/*
 * This class represents a daemon which fetches any updates from the ATC's update queue
 * and then hands these updates off to any TrafficSinks that are registered within the ATC
 * as handlers for updates from the update's source.
 */
public class OutputDaemon extends Thread {

		/* a value in milliseconds which represents how long this daemon should sleep in between waking up and checking the update queue */
		private long sleepInterval = 30000;
		
		/* boolean value to represent whether this thread should be alive. NOT REALLY USED! */
		private volatile boolean IS_ALIVE = true;
		private AirTrafficController atController;
		
		/*
		 * Constructor for the OutputDaemon
		 * @param sleepFor A value in milliseconds which represents how long the daemon should sleep in between checks. 
		 * @param atc An instance of an AirTrafficController which this daemon can fetch updates from and get info on registered sinks
		 */
		public OutputDaemon(long sleepFor, AirTrafficController atc) {
			this.sleepInterval = sleepFor;
			this.atController = atc;
		}
		
		public void doRevive()  {
			IS_ALIVE = true;
		}
		
		public void doSuspend() {
			IS_ALIVE = false;
		}
			
		/* Helper function to sleep for a specified period of time */
		public void sleepForInterval(long interval) {
			try { Thread.sleep(interval);} 
			catch (InterruptedException ie) {
				//TLog.log(Level.WARNING, "Error sleeping " + "\n" + ie.getMessage());
			}
		}
		
		/*
		 * (non-Javadoc)
		 * @see java.lang.Runnable#run()
		 */
		@Override
		public void run() {
			while (IS_ALIVE) {
				
				/*if (!IS_ALIVE) {
					try {
						TLog.log(Level.CONFIG, "OutputThread going to bed");
						TLog.log(Level.CONFIG, Thread.currentThread().getName());
						wait();
					} catch (InterruptedException e) {
						TLog.log(Level.CONFIG, "Output Thread done waiting!");
					}
				}*/
				
				TLog.log(Level.FINER, "<<<<Output Daemon updating traffic sinks>>>>");
				
				/** Examine the updates. Note, the udpates are removed from ATC's queue. If they're not acted on here,
				 * they're lost
				 **/
				List<TrafficUpdate> updates = atController.getAllUpdates();
				Level use_level;
				if (updates.size() > 0) use_level = Level.FINE;
				else use_level = Level.FINER;
				TLog.log(use_level, updates.size() + " updates to be handled");
				//logUpdates(updates);
				
				Iterator<TrafficUpdate> updateI = updates.iterator();
				while (updateI.hasNext()) {
					TrafficUpdate update = updateI.next();
				
					/** query the ATC to see if there are any sinks registered for the source of the update **/
					List<TrafficSink> registeredSinks = atController.getSinksForSource(update.getSource());
					TLog.log(Level.FINEST, registeredSinks.size() + " sinks found for update from source " + update.getSource().getName());
					
					/** No problem if there are none, just continue **/
					if (registeredSinks == null) {
						TLog.log(Level.WARNING, "No sinks found for updates from source " + update.getSource().getName());
						continue;
					}
					
					Iterator<TrafficSink> sinkI = registeredSinks.iterator();
					while (sinkI.hasNext()) {
						TrafficSink sink = sinkI.next();
						try {
							// attempt to handle the update. Will return false if the update is not handled because
							// the sink is over it's maximum number of updates per minute
							TLog.log(Level.FINEST, "Handing update '" + update.getMessage() + "' to sink " + sink.getName());
							boolean updated = sink.processUpdate(update);
						} catch (TrafficSinkException e) {
							TLog.log(Level.WARNING, 
									String.format("Error while handling update from source %s: %s", 
											update.getSource().getName(),
											e.getMessage()));
						}
					}
					
					update = null;
				}
			
				updates = null;
				updateI = null;
				
				// Run garbage collection here...
				System.gc();
				
				sleepForInterval(sleepInterval);
			}
		}		
		
		/*
		 * Utility function to print out a List of updates to the log.
		 */
		public void logUpdates(List<TrafficUpdate> updates) {
			TLog.log(Level.FINEST, "Received updates from ATC:");
			Iterator<TrafficUpdate> i = updates.iterator();
			while (i.hasNext()) {
				TLog.log(Level.FINEST, (i.next()).toString());
			}
		}
}
